{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 文件读写\n",
    "### 读文件\n",
    "要以读文件的模式打开一个文件对象，使用Python内置的open()函数，传入文件名和标示符："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f = open('file/about1.txt', 'r')\n",
    "print(f.read())\n",
    "f.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "标示符'r'表示读，这样，我们就成功地打开了一个文件。\n",
    "\n",
    "如果文件不存在，open()函数就会抛出一个IOError的错误，并且给出错误码和详细的信息告诉你文件不存在"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f = open('file/about.txt', 'r')\n",
    "# 如果文件打开成功，接下来，调用read()方法可以一次读取文件的全部内容，Python把内容读到内存，用一个str对象表示：\n",
    "f.readline() # 读取文件的第一行"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f.readline() # 再运行一次自动读取下一行"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "最后一步是调用close()方法关闭文件。文件使用完毕后必须关闭，\n",
    "\n",
    "因为文件对象会占用操作系统的资源，并且操作系统同一时间能打开的文件数量也是有限的："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f.close()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 已经关闭，f就不能再操作\n",
    "f.readline()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "由于文件读写时都有可能产生IOError，一旦出错，后面的f.close()就不会调用。\n",
    "\n",
    "所以，为了保证无论是否出错都能正确地关闭文件，我们可以使用try ... finally来实现："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "try:\n",
    "    # try里面如果出现错误，程序就会来到except里面\n",
    "    f = open('file/hello.txt', 'r')\n",
    "    print(\"成功打开文档\")\n",
    "    print(\"文档内容:\", f.read())\n",
    "except:\n",
    "    # 出错程序就从这里开始\n",
    "    print(\"读取文档出错\")\n",
    "finally:\n",
    "    # 程序总会来到这里\n",
    "    print(\"总会来这里\")\n",
    "    # [],{},0,None都表示False,\n",
    "    if f:  \n",
    "        print(\"文档有打开，这里来关闭\")\n",
    "        f.close()\n",
    "    else:\n",
    "        print(\"没有发现文档\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "但是每次都这么写实在太繁琐，所以，Python引入了with语句来自动帮我们调用close()方法："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "with open('file/hello.txt', 'r') as f:\n",
    "    print(f.read())\n",
    "# 这和前面的try ... finally是一样的，但是代码更佳简洁，并且不必调用f.close()方法。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "调用read()会一次性读取文件的全部内容，如果文件有10G，内存就爆了，\n",
    "\n",
    "所以，要保险起见，可以反复调用read(size)方法，每次最多读取size个字节的内容。另外，\n",
    "\n",
    "调用readline()可以每次读取一行内容，调用readlines()一次读取所有内容并按行返回list。因此，要根据需要决定怎么调用。\n",
    "\n",
    "如果文件很小，read()一次性读取最方便；如果不能确定文件大小，反复调用read(size)比较保险；\n",
    "\n",
    "如果是配置文件，调用readlines()最方便："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "with open('file/about.txt', 'r') as f:\n",
    "    for line in f.readlines():\n",
    "        print(line.strip()) # 把末尾的'\\n'删掉"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 遇到有些编码不规范的文件，你可能会遇到UnicodeDecodeError，因为在文本文件中可能夹杂了一些非法编码的字符。\n",
    "# 遇到这种情况，open()函数还接收一个errors参数，表示如果遇到编码错误后如何处理。最简单的方式是直接忽略：\n",
    "f = open('file/about.txt', 'r', encoding='gbk', errors='ignore')\n",
    "print(f.read())\n",
    "f.close()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#-- 文件基本操作，这里不能运行 \n",
    "    output = open(r'C:\\spam', 'w')          # 打开输出文件，用于写\n",
    "    input = open('data', 'r')               # 打开输入文件，用于读。打开的方式可以为'w', 'r', 'a', 'wb', 'rb', 'ab'等\n",
    "    fp.read([size])                         # size为读取的长度，以byte为单位\n",
    "    fp.readline([size])                     # 读一行，如果定义了size，有可能返回的只是一行的一部分\n",
    "    fp.readlines([size])                    # 把文件每一行作为一个list的一个成员，并返回这个list。其实它的内部是通过循环调用readline()来实现的。如果提供size参数，size是表示读取内容的总长。\n",
    "    fp.readable()                           # 是否可读\n",
    "    fp.write(str)                           # 把str写到文件中，write()并不会在str后加上一个换行符\n",
    "    fp.writelines(seq)                      # 把seq的内容全部写到文件中(多行一次性写入)\n",
    "    fp.writeable()                          # 是否可写\n",
    "    fp.close()                              # 关闭文件。\n",
    "    fp.flush()                              # 把缓冲区的内容写入硬盘\n",
    "    fp.fileno()                             # 返回一个长整型的”文件标签“\n",
    "    fp.isatty()                             # 文件是否是一个终端设备文件（unix系统中的）\n",
    "    fp.tell()                               # 返回文件操作标记的当前位置，以文件的开头为原点\n",
    "    fp.next()                               # 返回下一行，并将文件操作标记位移到下一行。把一个file用于for … in file这样的语句时，就是调用next()函数来实现遍历的。\n",
    "    fp.seek(offset[,whence])                # 将文件打开操作标记移到offset的位置。whence为0表示从头开始计算，1表示以当前位置为原点计算。2表示以文件末尾为原点进行计算。\n",
    "    fp.seekable()                           # 是否可以seek\n",
    "    fp.truncate([size])                     # 把文件裁成规定的大小，默认的是裁到当前文件操作标记的位置。\n",
    "    for line in open('data'): \n",
    "        print(line)                         # 使用for语句，比较适用于打开比较大的文件\n",
    "    with open('data') as file:\n",
    "        print(file.readline())              # 使用with语句，可以保证文件关闭\n",
    "    with open('data') as file:\n",
    "        lines = file.readlines()            # 一次读入文件所有行，并关闭文件\n",
    "    open('f.txt', encoding = 'latin-1')     # Python3.x Unicode文本文件\n",
    "    open('f.bin', 'rb')                     # Python3.x 二进制bytes文件，如图片，视频\n",
    "    # 文件对象还有相应的属性：buffer closed encoding errors line_buffering name newlines等\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 操作文件和目录\n",
    "\n",
    "如果我们要操作文件、目录，可以在命令行下面输入操作系统提供的各种命令来完成。比如dir、cp等命令。\n",
    "\n",
    "如果要在Python程序中执行这些目录和文件的操作怎么办？\n",
    "\n",
    "其实操作系统提供的命令只是简单地调用了操作系统提供的接口函数，Python内置的os模块也可以直接调用操作系统提供的接口函数。\n",
    "\n",
    "打开Python交互式命令行，我们来看看如何使用os模块的基本功能："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "print(\"操作系统\", os.name)   # 如果是posix，说明系统是Linux、Unix或Mac OS X，如果是nt，就是Windows系统。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 要获取详细的系统信息\n",
    "# 注意uname()函数在Windows上不提供，也就是说，os模块的某些函数是跟操作系统相关的\n",
    "print(os.uname())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "操作文件和目录的函数一部分放在os模块中，一部分放在os.path模块中，这一点要注意一下。查看、创建和删除目录可以这么调用："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 查看当前目录的绝对路径:\n",
    "path = os.path.abspath('.')\n",
    "print(\"当前目录\", path)\n",
    "# 在当前目录下创建一个新目录，首先把新目录的完整路径表示出来:\n",
    "new_path = os.path.join(path, 'testdir')\n",
    "print(\"新目录\", new_path)\n",
    "# 然后创建一个目录:\n",
    "os.mkdir(new_path)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 删掉一个目录:\n",
    "os.rmdir(new_path)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "把两个路径合成一个时，不要直接拼字符串，而要通过os.path.join()函数，这样可以正确处理不同操作系统的路径分隔符。\n",
    "\n",
    "在Linux/Unix/Mac下，os.path.join()返回这样的字符串：\n",
    "\n",
    "part-1/part-2\n",
    "\n",
    "而Windows下会返回这样的字符串：\n",
    "\n",
    "part-1\\part-2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 文件操作使用下面的函数。假定当前目录下有一个test.txt文件：\n",
    "os.rename('test.txt', 'test.py')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 删掉文件:\n",
    "os.remove('test.py')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 要列出当前目录下的所有目录\n",
    "for x in os.listdir('.'):\n",
    "    print(x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "dir(os) # 这里有非常多功能，请同学自己去使用中学习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 练习：　\n",
    "\"\"\"\n",
    "然后在里面创建１０个ｔｘｔ，里面写入１－１００的数字，每行５个数字\n",
    "在当前目录下创建一个ｔｅｓｔ文件夹，\n",
    "如：\n",
    "１　２　３　４　５\n",
    "６　７　８　９　１０\n",
    "\"\"\"\n",
    "path = os.path.abspath('.')\n",
    "new_path = os.path.join(path, ???)\n",
    "\n",
    "try:\n",
    "    ???\n",
    "except:\n",
    "    print(\"已经存在，不需要创建\")\n",
    "\n",
    "for i in range(10):\n",
    "    path = ???\n",
    "    with open(path, 'w') as f:\n",
    "        step = ???\n",
    "        for j in range(???):\n",
    "            for k in range(???):\n",
    "                f.write(???)\n",
    "                step = ???\n",
    "            f.write(???)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
